package com.atguigu.gmall.cart.service.impl;

import com.alibaba.fastjson.JSON;
import com.atguigu.gmall.cart.bean.CartInfo;
import com.atguigu.gmall.cart.bean.UserInfo;
import com.atguigu.gmall.cart.feign.GmallPmsClient;
import com.atguigu.gmall.cart.feign.GmallSmsClient;
import com.atguigu.gmall.cart.feign.GmallWmsClient;
import com.atguigu.gmall.cart.interceptor.LoginInterceptor;
import com.atguigu.gmall.cart.mapper.CartInfoMapper;
import com.atguigu.gmall.cart.service.CartService;
import com.atguigu.gmall.common.bean.ResponseVo;
import com.atguigu.gmall.common.consts.GmallConsts;
import com.atguigu.gmall.common.exception.GmallUserException;
import com.atguigu.gmall.pms.entity.SkuAttrValueEntity;
import com.atguigu.gmall.pms.entity.SkuEntity;
import com.atguigu.gmall.sms.vo.ItemSaleVO;
import com.atguigu.gmall.wms.entity.WareSkuEntity;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.util.concurrent.ListenableFuture;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
public class CartServiceImpl implements CartService {
    @Resource
    StringRedisTemplate stringRedisTemplate;
    @Resource
    GmallPmsClient pmsClient;
    @Resource
    GmallWmsClient wmsClient;
    @Resource
    GmallSmsClient smsClient;
    @Resource
    CartAsyncServiceImpl cartAsyncService;
    public void renew(){
        String userKey = getUserKey();
        stringRedisTemplate.expire(GmallConsts.CART_PREFIX+userKey ,7, TimeUnit.DAYS);
    }

    //添加商品到购物车
    @Override
    public void add2Cart(Long skuId, Integer count) {
        renew();
        //1、获取用户的购物车
        //获取userId/userKey
        String userKey = getUserKey();
        //获取用户的购物车： cart:userKey
        String cartKey = "cart:"+userKey;
        BoundHashOperations<String, String, String> hashOps = //相当于存购物项数据的map(skuId->cartItem)
                stringRedisTemplate.boundHashOps(cartKey);
        //2、判断购物车中是否存在skuId对应的购物项
        if(hashOps.hasKey(skuId.toString())){
            //如果存在 更新数量
            String cartInfoJsonStr = hashOps.get(skuId.toString());//根据skuId查询当前用户购物车中的一个购物项json字符串
            CartInfo cartInfo = JSON.parseObject(cartInfoJsonStr, CartInfo.class);
            cartInfo.setCount(cartInfo.getCount()+count);
            hashOps.put(skuId.toString() , JSON.toJSONString(cartInfo));
            //异步更新数据库购物项数量
            cartAsyncService.updateCartInfoCount(userKey,skuId.toString(), cartInfo);
        }else{
            //如果不存在 新创建购物项对象存到购物车中
            CartInfo cartInfo = new CartInfo();
            cartInfo.setUserId(userKey);
            cartInfo.setCount(count);
            cartInfo.setCheck(false);
            cartInfo.setSkuId(skuId.toString());
            //远程访问 pms 根据skuId查询sku数据
            ResponseVo<SkuEntity> skuEntityResponseVo = pmsClient.querySkuById(skuId);
            SkuEntity skuEntity = skuEntityResponseVo.getData();
            if(skuEntity==null){
                throw new GmallUserException(skuId+"商品不存在",1006);
            }
            cartInfo.setDefaultImage(skuEntity.getDefaultImage());
            cartInfo.setPrice(skuEntity.getPrice());
            cartInfo.setTitle(skuEntity.getTitle());
            //远程访问 wms 根据skuId查询库存
            ResponseVo<List<WareSkuEntity>> wareSkuResponseVo = wmsClient.listBySkuId(skuId);
            List<WareSkuEntity> wareSkuEntities = wareSkuResponseVo.getData();
            if(!CollectionUtils.isEmpty(wareSkuEntities)){
                cartInfo.setStore(wareSkuEntities.stream().anyMatch(wareSkuEntity ->
                        wareSkuEntity.getStock()-wareSkuEntity.getStockLocked()>0));
            }
            //远程访问 sms 根据skuId查询营销数据
            ResponseVo<List<ItemSaleVO>> skuSalesResponseVo = smsClient.querySkuSalesBySkuId(skuId);
            List<ItemSaleVO> itemSaleVOS = skuSalesResponseVo.getData();
            if(!CollectionUtils.isEmpty(itemSaleVOS)){
                cartInfo.setSales(JSON.toJSONString(itemSaleVOS));
            }
            //远程访问pms 根据skuId查询当前sku的销售属性
            ResponseVo<List<SkuAttrValueEntity>> skuAttrValuesResponseVo = pmsClient.searchAttrValues(skuId);
            List<SkuAttrValueEntity> skuAttrValueEntities = skuAttrValuesResponseVo.getData();
            if(!CollectionUtils.isEmpty(skuAttrValueEntities)){
                cartInfo.setSaleAttrs(JSON.toJSONString(skuAttrValueEntities));
            }
            hashOps.put(skuId.toString(), JSON.toJSONString(cartInfo));
            //异步新增购物项
            cartAsyncService.saveCartInfo(userKey,cartInfo);

        }



    }




    @Override
    public CartInfo queryCartInfoBySkuId(String skuId) {
        renew();
        String userKey = getUserKey();
        String cartKey = "cart:"+userKey;
        BoundHashOperations<String, String, String> hashOps = stringRedisTemplate.boundHashOps(cartKey);
        String cartInfoJson = hashOps.get(skuId);
        if(StringUtils.isEmpty(cartInfoJson)){
            throw new GmallUserException("商品信息不存在",1008);
        }
        return JSON.parseObject(cartInfoJson , CartInfo.class);
    }

    private String getUserKey(){
        UserInfo userInfo = LoginInterceptor.userInfo();
        return userInfo.getUserId()==null?userInfo.getUserKey()
                :userInfo.getUserId().toString();

    }





    @Override
    public List<CartInfo> queryCartInfos() {
        renew();
        UserInfo userInfo = LoginInterceptor.userInfo();
        //1、根据userKey获取未登录的购物车数据
        String userKey = userInfo.getUserKey();
        String cartKey = "cart:"+userKey;
        BoundHashOperations<String, String, String> unloginHashOps = stringRedisTemplate.boundHashOps(cartKey);
        List<CartInfo> unloginCartInfos = null;
        if(unloginHashOps.size()>0){
            //存在未登录购物车数据
            List<String> unloginCartInfoJsons = unloginHashOps.values();
            unloginCartInfos = unloginCartInfoJsons.stream().map(unloginCartInfoJson->{
                //将一个购物项json字符串转为购物项对象
                return JSON.parseObject(unloginCartInfoJson,CartInfo.class);
            }).collect(Collectors.toList());
        }
        //2、如果userId为空代表用户未登录： 返回未登录购物车
        Long userId = userInfo.getUserId();
        if(userId==null){
            //用户未登录：返回未登录购物车
            return unloginCartInfos;
        }
        //3、用户已登录：根据userId查询已登录的购物车数据
        cartKey = "cart:"+userId;
        BoundHashOperations<String, String, String> loginHashOps = stringRedisTemplate.boundHashOps(cartKey);
        //4、如果未登录购物车为空：返回已登录购物车数据
        //解析已登录的购物车数据：
        List<CartInfo> loginCartInfos = null;
        if(loginHashOps.size()>0){
            List<String> loginCartInfoJsons = loginHashOps.values();
            loginCartInfos = loginCartInfoJsons.stream().map(loginCartInfoJson-> JSON.parseObject(loginCartInfoJson , CartInfo.class)
            ).collect(Collectors.toList());
        }
        //未登录购物车为空：返回已登录购物车数据
        if(CollectionUtils.isEmpty(unloginCartInfos)){
            return loginCartInfos;
        }
        //5、如果未登录购物车不为空： 将未登录购物车数据合并到已登录购物车中
        //  已登录购物车中存在未登录购物车购物项，则更新数据同步到数据库，如果不存在 将购物项新增存到已登录购物车中
        //5.1 如果已登录购物车为空 + 未登录购物车一定不为空
        // 直接将未登录购物车所有的购物项添加到已登录购物车中 返回
        if(CollectionUtils.isEmpty(loginCartInfos)){
            //修改未登录购物项的userId为已登录的用户id
            unloginCartInfos.forEach(unloginCartInfo->{
                unloginCartInfo.setUserId(userId.toString());
            });
            //删除redis中未登录的购物车
            stringRedisTemplate.delete("cart:"+userKey);
            //将已登录购物车的数据存入到redis中
            unloginCartInfos.forEach(cartInfo -> {
                loginHashOps.put(cartInfo.getSkuId().toString(),
                        JSON.toJSONString(cartInfo));
            });
            //TODO：更新 mysql中的未登录的购物车数据为已登录购物车数据
            // 修改购物项的userId
            cartAsyncService.updateCartInfosUserId(userKey,unloginCartInfos);
            return unloginCartInfos;
        }
        //5.2 如果已登录购物车不为空  + 未登录购物车一定不为空：合并购物车
        //未登录购物车中只有两条记录 遍历只会执行两次
//        List<CartInfo> allCartInfos = new ArrayList<>();
        Map<String, String> loginCartInfoMap = loginHashOps.entries();
        List<CartInfo> updateCartInfosSkuId = new ArrayList<>();
        unloginCartInfos.forEach(unloginCartInfo->{
            unloginCartInfo.setUserId(userId.toString());
            //获取已登录的购物项
            String cartInfoJson = loginCartInfoMap.get(unloginCartInfo.getSkuId().toString());
            if(StringUtils.isEmpty(cartInfoJson)){
                //该未登录的购物项 在已登录购物车中不存在: 新增
                loginCartInfoMap.put(unloginCartInfo.getSkuId().toString(),
                        JSON.toJSONString(unloginCartInfo));
                updateCartInfosSkuId.add(unloginCartInfo);
            }else{
                //该未登录的购物项 在已登录的购物车中存在: 更细数量
                CartInfo loginCartInfo = JSON.parseObject(cartInfoJson, CartInfo.class);
                loginCartInfo.setCount(loginCartInfo.getCount()+unloginCartInfo.getCount());
                loginCartInfoMap.put(loginCartInfo.getSkuId().toString() ,
                        JSON.toJSONString(loginCartInfo));
                //删除未登录的购物项  更新已登录购物项数量
                cartAsyncService.deleteAndUpdateCartInfo(userKey,unloginCartInfo,loginCartInfo);
            }
        });
        //将合并后的map的数据存入到已登录的购物车中：如果key相同覆盖之前的数据
        loginHashOps.putAll(loginCartInfoMap);

        //TODO: 删除合并掉的未登录的购物项数据
        cartAsyncService.updateCartInfosUserId(userKey,updateCartInfosSkuId);
        //6、删除未登录购物车数据
        stringRedisTemplate.delete("cart:"+userKey);

        //7、返回合并后的购物车
        return  loginCartInfoMap.values().stream().map(cartInfoJson->
                JSON.parseObject(cartInfoJson , CartInfo.class)
        ).collect(Collectors.toList());
    }

    @Override
    public void updateNum(CartInfo cartInfo) {
        renew();
        //更新redis中购物项数据
        // 访问购物车页面数据时 如果用户已登录 一定会合并购物车
        // 有userId使用已登录userId获取购物车数据  否则使用userKey
        String userKey = getUserKey();
        BoundHashOperations<String, String, String> hashOps = stringRedisTemplate.boundHashOps("cart:" + userKey);
        String cartInfoJson = hashOps.get(cartInfo.getSkuId().toString());
        if(cartInfo.getCount()<=0 || StringUtils.isEmpty(cartInfoJson)){
            throw new GmallUserException("购物项不存在/数量必须超过0" , 1009);
        }
        //更新到redis中
        CartInfo c = JSON.parseObject(cartInfoJson, CartInfo.class);
        c.setCount(cartInfo.getCount());
        hashOps.put(cartInfo.getSkuId().toString() , JSON.toJSONString(c));
        //更新到数据库中
        // 参数1：要更新数据库中的购物项的skuId，参数2：要更新数据库中的购物项所属的用户key/id ,参数3：携带要更新的数量
        cartAsyncService.updateCartInfoCount(userKey ,cartInfo.getSkuId() ,c);

    }

    @Override
    public void updateStatus(CartInfo cartInfo) {
        renew();
        String userKey = getUserKey();
        BoundHashOperations<String, String, String> hashOps = stringRedisTemplate.boundHashOps("cart:" + userKey);
        String cartInfoJson = hashOps.get(cartInfo.getSkuId());
        if(StringUtils.isEmpty(cartInfoJson)){
            throw new GmallUserException("购物项不存在" , 1009);
        }
        //更新到redis中
        CartInfo c = JSON.parseObject(cartInfoJson, CartInfo.class);
        c.setCheck(cartInfo.getCheck());
        hashOps.put(cartInfo.getSkuId() , JSON.toJSONString(c));
        //更新到数据库中
        // 参数1：要更新数据库中的购物项的skuId，参数2：要更新数据库中的购物项所属的用户key/id ,参数3：携带要更新的数量
        cartAsyncService.updateCartInfoBySkuIdAndUserKey(userKey ,cartInfo.getSkuId() ,c);
    }

    @Override
    public void deleteCartInfoBySkuId(String skuId) {
        renew();
        String userKey = getUserKey();
        //查找当前用户的购物车
        BoundHashOperations<String, String, String> hashOps = stringRedisTemplate.boundHashOps(GmallConsts.CART_PREFIX + userKey);
        //删除购物车中skuId对应的购物项
        hashOps.delete(skuId);
        //异步删除数据库中的购物项
        cartAsyncService.deleteCartInfoBySkuId(userKey,skuId);
    }

    @Override
    public void batchDelCartInfos() {
        renew();
        //获取用户的购物车
        String userKey = getUserKey();
        BoundHashOperations<String, String, String> hashOps = stringRedisTemplate.boundHashOps(GmallConsts.CART_PREFIX + userKey);
        if(hashOps.size()==0){
            throw new GmallUserException("购物车中没有商品" , 1011);
        }
        //获取购物车中选中的 购物项的 skuId集合
        List<String> checkedCartInfoSkuIds = hashOps.values().stream().map(json -> JSON.parseObject(json, CartInfo.class))
                .filter(cartInfo -> cartInfo.getCheck())
                .map(CartInfo::getSkuId).collect(Collectors.toList());
        if(CollectionUtils.isEmpty(checkedCartInfoSkuIds)){
            throw new GmallUserException("请选择要删除的商品" , 1011);
        }
        //集合不为空
        //redis中批量删除
        hashOps.delete(checkedCartInfoSkuIds.toArray());
        cartAsyncService.batchDelCartInfo(userKey,checkedCartInfoSkuIds);
    }

    @Override
    public List<CartInfo> queryCheckedCartInfosByUserId(Long userId) {
        BoundHashOperations<String, String, String> hashOps = stringRedisTemplate.boundHashOps(
                GmallConsts.CART_PREFIX + userId);
        if(hashOps.size()==0)
            return null;
        return hashOps.values().stream().map(cartInfoJson-> JSON.parseObject(cartInfoJson,CartInfo.class))
                  .filter(cartInfo -> cartInfo.getCheck()).collect(Collectors.toList());
    }


    @Async
    @Override
    public String execution1() {
        try {
            System.out.println("exe1开始....");
            Thread.sleep(3000);
            System.out.println("exe1结束....");
            return "exe1-result";
        } catch (InterruptedException e) {
            return null;
        }
    }
    @Async
    @Override
    public String execution2() {
        try {
            System.out.println("exe2开始....");
            Thread.sleep(5000);
            int i = 1/0;
            System.out.println("exe2结束....");
            return "exe2-result";
        } catch (InterruptedException e) {
            return null;
        }
    }

}
